/**
 * 
 */
package algorithm;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;

import javax.swing.JOptionPane;

import comparators.AppointmentComperatorTime;
import comparators.DateComparator;

import entities.Appointment;
import entities.Calendar;
import entities.Category;
import exceptions.KeinFreierTermin;
import fachlogik.Administration;

/**
 * @author Alex & Veit
 * 
 */
public class FinalTool implements ToolInterface {

private Administration         admin;
private ArrayList<Appointment> alleFreienTermine;

/**
 * 
 */
public FinalTool() {
    SimpleDateFormat sdfD = new SimpleDateFormat("dd.MM.yyyy");
    SimpleDateFormat sdfH = new SimpleDateFormat("HH:mm");
    alleFreienTermine = new ArrayList<Appointment>();

    this.admin = Administration.getInstance();
}

/*
 * (non-Javadoc)
 * @see algorithm.ToolInterface#listToReschedule(java.util.List)
 */
@Override
public void listToReschedule(List<Appointment> liste) {
    // TODO Auto-generated method stub

}

@Override
public Date findGap(Appointment a, Date von, Date bis) throws KeinFreierTermin {
    ArrayList<Appointment> alleApps = new ArrayList<>();
    Date zielDatum = null;
    for (Appointment app1 : admin.getCalendarAppointments(a.getCalendar())) {
        alleApps.add(app1);
    }
    // admin.appointmentsSortieren(alleApps);

    Collections.sort(alleApps, new AppointmentComperatorTime());
    ArrayList<Date> tmpTage = tageAuflisten(von, bis);
    ArrayList<Date> gelisteteTage = new ArrayList<>();

    if (a.getCategory().getWochenendeErlaubt() == false) {
        for (Date d : tmpTage) {
            if (d.getDay() != 0 && d.getDay() != 6) {
                gelisteteTage.add(d);
            }
        }
    }
    else {
        for (Date d : tmpTage)
            gelisteteTage.add(d);
    }

    ArrayList<Appointment> zeitfensterListe = categoryAppErstellen(gelisteteTage, a.getCategory());
    System.out.println("app");
    System.out.println(a.getBegin());
    System.out.println(a.getEnd());
    System.out.println("cat");
    System.out.println(a.getCategory().getAnfangszeit());
    System.out.println(a.getCategory().getEndzeit());
    System.out.println("gelisteteTage");
    System.out.println(gelisteteTage);
    System.out.println("zeitfensterListe");
    System.out.println(zeitfensterListe);

    for (Appointment app1 : zeitfensterListe) {
        for (Appointment b : freieTermine(app1, alleApps)) {
            alleFreienTermine.add(b);
        }
    }
    
    Collections.sort(alleFreienTermine, new AppointmentComperatorTime());
    
    for (int i = 0; i <= alleFreienTermine.size(); i++) {
        if (a.getLength() <= alleFreienTermine.get(i).getLength()) {
            zielDatum = alleFreienTermine.get(i).getBegin();
            return zielDatum;
        }
    }
    
//    for (Appointment app2 : alleFreienTermine) {
//        if (a.getLength() <= app2.getLength()) {
//            zielDatum = app2.getBegin();
//            return zielDatum;
//        }
//    }
    throw new KeinFreierTermin("No suitable timeslot for " + a.getName() + " found.");
}

/**
 * 
 * @param zeitfenster
 * @param besetzteApps
 * @return freieTermine (invertierte)
 */

@SuppressWarnings("unchecked")
public ArrayList<Appointment> freieTermine(Appointment zeitfenster, List<Appointment> besetzteApps) {
    System.out.println("Anfang");
    System.out.println("bestetzeApps");
    System.out.println(besetzteApps);
    System.out.println("zeitfenster");
    System.out.println(zeitfenster);
    ArrayList<Appointment> relevanteTermine = relevanteTermine(zeitfenster, besetzteApps);

    // admin.appointmentsSortieren(relevanteTermine);
    Collections.sort(relevanteTermine, new AppointmentComperatorTime());

    ArrayList<Appointment> freieTermine = new ArrayList<>();

    if (relevanteTermine.size() == 0) {
        freieTermine.add(zeitfenster);
        return freieTermine;
    }

    // falls es nur einen relevanten Termin gibt
    if (relevanteTermine.size() == 1) {
        Appointment a = new Appointment();
        Appointment b = new Appointment();

        // 1. Fall:
        // der einzige relevante Termin überschneidet sich mit dem Anfang des
        // Zeitfensters
        // Überprüfung, ob sein Anfang vor Anfang Zeitfenster liegt
        if (relevanteTermine.get(0).getBegin().getTime() < zeitfenster.getBegin().getTime()) {
            a.setBegin(relevanteTermine.get(0).getEnd());
            a.setEnd(zeitfenster.getEnd());
            freieTermine.add(a);

            System.out.println("ende");
            System.out.println("bestetzeApps");
            System.out.println(besetzteApps);
            System.out.println("zeitfenster");
            System.out.println(zeitfenster);
            System.out.println("freieTermine");
            System.out.println(freieTermine);
            return freieTermine;
        }

        // 2. Fall
        // der einzige relevante Termin überschneidet sich mit dem Ende des
        // Zeitfensters
        // Überprüfung, ob sein Ende nach Ende Zeifenster liegt
        if (relevanteTermine.get(0).getEnd().getTime() > zeitfenster.getEnd().getTime()) {
            a.setBegin(zeitfenster.getBegin());
            a.setEnd(relevanteTermine.get(0).getBegin());
            freieTermine.add(a);

            System.out.println("ende");
            System.out.println("bestetzeApps");
            System.out.println(besetzteApps);
            System.out.println("zeitfenster");
            System.out.println(zeitfenster);
            System.out.println("freieTermine");
            System.out.println(freieTermine);
            return freieTermine;
        }

        // 3. Fall
        // Termin liegt irgendwo im Zeitfenster
        // d.h. ein freier Termin vom Anfang Zeitfenster bis Anfang relevanter
        // Termin
        // 2. freier Termin vom Ende Termin bis Ende Zeitfenster
        a.setBegin(zeitfenster.getBegin());
        a.setEnd(relevanteTermine.get(0).getBegin());
        freieTermine.add(a);

        b.setBegin(relevanteTermine.get(0).getEnd());
        b.setEnd(zeitfenster.getEnd());
        freieTermine.add(b);
        

        System.out.println("ende");
        System.out.println("bestetzeApps");
        System.out.println(besetzteApps);
        System.out.println("zeitfenster");
        System.out.println(zeitfenster);
        System.out.println("freieTermine");
        System.out.println(freieTermine);
        return freieTermine;
    }

    // falls der erste relevante Termin nach Anfang des Zeitfensters beginnt
    // d.h. ein freier Termin zwischen Zeitfenster-Anfang und Anfang erster
    // relevanter Termin
    if (relevanteTermine.get(0).getBegin().getTime() > zeitfenster.getBegin().getTime()) {
        Appointment a = new Appointment();
        a.setBegin(zeitfenster.getBegin());
        a.setEnd(relevanteTermine.get(0).getBegin());
        freieTermine.add(a);
    }

    // falls der letzte relevante Termin vor Ende des Zeitfensters aufhört
    // d.h. ein freier Termin zwischen Ende letzter relevanter Termin und Ende
    // Zeitfenster
    if (relevanteTermine.get(relevanteTermine.size() - 1).getEnd().getTime() < zeitfenster.getEnd()
            .getTime()) {
        Appointment a = new Appointment();
        a.setBegin(relevanteTermine.get(relevanteTermine.size() - 1).getEnd());
        a.setEnd(zeitfenster.getEnd());
        freieTermine.add(a);
    }

    for (int i = 0; i < relevanteTermine.size() - 1; i++) {
        if (relevanteTermine.get(i).getEnd() != relevanteTermine.get(i + 1).getBegin()) {
            Appointment a = new Appointment();
            a.setBegin(relevanteTermine.get(i).getEnd());
            a.setEnd(relevanteTermine.get(i + 1).getBegin());
            freieTermine.add(a);
        }
    }

    System.out.println("ende");
    System.out.println("bestetzeApps");
    System.out.println(besetzteApps);
    System.out.println("zeitfenster");
    System.out.println(zeitfenster);
    System.out.println("freieTermine");
    System.out.println(freieTermine);
    
    return freieTermine;
}

public ArrayList<Appointment> relevanteTermine(Appointment zeitfenster,
        List<Appointment> besetzteApps) {
    ArrayList<Appointment> relevanteTermine = new ArrayList<>();
    for (Appointment a : besetzteApps) {
        if (datumInnerhalbApp(a.getBegin(), zeitfenster)
                || datumInnerhalbApp(a.getEnd(), zeitfenster)) {
            relevanteTermine.add(a);
        }
    }
    return relevanteTermine;
}

/**
 * Gibt true wenn Datum innerhalb eines Termins ist.
 * 
 * @param a
 * @param d
 * @return
 */
public boolean datumInnerhalbApp(Date d, Appointment a) {
    if (a.getBegin().getTime() <= d.getTime() && a.getEnd().getTime() >= d.getTime())
        return true;
    return false;
}

public boolean datumReallyInnerhalbApp(Date d, Appointment a) {
    if (a.getBegin().getTime() < d.getTime() && a.getEnd().getTime() > d.getTime())
        return true;
    return false;
}

public ArrayList<Appointment> categoryAppErstellen(ArrayList<Date> dates, Category c) {
    ArrayList<Appointment> apps = new ArrayList<>();

    for (int i = 0; i < dates.size(); i++) {
        Appointment a = new Appointment();
        Date anfang = new Date(0);
        Date ende = new Date(0);
        anfang.setTime(c.getAnfangszeit().getTime() + dates.get(i).getTime() + 1000 * 60 * 60);
        ende.setTime(c.getEndzeit().getTime() + dates.get(i).getTime() + 1000 * 60 * 60);
        a.setBegin(anfang);
        a.setEnd(ende);
        apps.add(a);
    }
    Collections.sort(apps, new AppointmentComperatorTime());
    return apps;
}

public ArrayList<Date> tageAuflisten(Date d1, Date d2) {
    ArrayList<Date> dates = new ArrayList<>();
    long tag = 24 * 60 * 60 * 1000;
    while (d1.getTime() <= d2.getTime()) {
        Date d = new Date();
        d.setTime(d1.getTime());
        dates.add(d);
        d1.setTime(d1.getTime() + tag);
    }
    Collections.sort(dates, new DateComparator());
    return dates;
}

public boolean overlap(Appointment selectedAppointment) {
    ArrayList<Appointment> alleApps = new ArrayList<>();
//    ArrayList<Appointment> overlappingApps = new ArrayList<>();
    for (Appointment a : admin.getCalendarAppointments(selectedAppointment.getCalendar()))
    {
        if(a.getId() != selectedAppointment.getId())
            alleApps.add(a);
    }
    System.out.println(alleApps);
    

    for (Appointment a : alleApps)
    {
        if(Math.min(a.getEnd().getTime(), selectedAppointment.getEnd().getTime()) >= Math.max(a.getBegin().getTime(), selectedAppointment.getBegin().getTime()))
        {
            JOptionPane.showMessageDialog(null, "The appointments are overlapping");
            return false;
//          overlappingApps.add(a);
        }
//    return overlappingApps;
    }
    return true;
}

}
